package com.gitee.wsl.android.ext.base

import android.graphics.PointF
import android.graphics.Rect
import android.graphics.RectF
import androidx.compose.ui.geometry.Size
import com.gitee.wsl.ext.math.PI_RAD
import com.gitee.wsl.ext.math.piRad
import com.gitee.wsl.ext.number.half
import kotlin.math.abs
import kotlin.math.cos
import kotlin.math.sin

object RectExt

/**
 * Helper method to set rectOut with rectFSrc.
 */
fun RectExt.setRect(rectFSrc: RectF, rectOut: Rect) {
    rectOut.left = rectFSrc.left.toInt()
    rectOut.top = rectFSrc.top.toInt()
    rectOut.right = rectFSrc.right.toInt()
    rectOut.bottom = rectFSrc.bottom.toInt()
}

fun RectExt.scaleRectFAboutCenter(r: RectF, scale: Float) {
    scaleRectFAboutPivot(r, scale, r.centerX(), r.centerY())
}

fun RectExt.scaleRectFAboutPivot(r: RectF, scale: Float, px: Float, py: Float) {
    if (scale != 1.0f) {
        r.offset(-px, -py)
        r.left = r.left * scale
        r.top = r.top * scale
        r.right = r.right * scale
        r.bottom = r.bottom * scale
        r.offset(px, py)
    }
}

fun RectExt.scaleRectAboutCenter(r: Rect, scale: Float) {
    if (scale != 1.0f) {
        val cx = r.centerX()
        val cy = r.centerY()
        r.offset(-cx, -cy)
        scaleRect(r, scale)
        r.offset(cx, cy)
    }
}

fun RectExt.scaleRect(r: Rect, scale: Float) {
    if (scale != 1.0f) {
        r.left = (r.left * scale + 0.5f).toInt()
        r.top = (r.top * scale + 0.5f).toInt()
        r.right = (r.right * scale + 0.5f).toInt()
        r.bottom = (r.bottom * scale + 0.5f).toInt()
    }
}

fun RectExt.insetRect(r: Rect, insets: Rect) {
    r.left = r.right.coerceAtMost(r.left + insets.left)
    r.top = r.bottom.coerceAtMost(r.top + insets.top)
    r.right = r.left.coerceAtLeast(r.right - insets.right)
    r.bottom = r.top.coerceAtLeast(r.bottom - insets.bottom)
}

fun RectExt.shrinkRect(r: Rect, scaleX: Float, scaleY: Float): Float {
    val scale = scaleX.coerceAtMost(scaleY).coerceAtMost(1.0f)
    if (scale < 1.0f) {
        val deltaX = (r.width() * (scaleX - scale) * 0.5f).toInt()
        r.left += deltaX
        r.right -= deltaX
        val deltaY = (r.height() * (scaleY - scale) * 0.5f).toInt()
        r.top += deltaY
        r.bottom -= deltaY
    }
    return scale
}

/**
 * Similar to [.scaleRectAboutCenter] except this allows different scales
 * for X and Y
 */
fun RectExt.scaleRectFAboutCenter(r: RectF, scaleX: Float, scaleY: Float) {
    val px = r.centerX()
    val py = r.centerY()
    r.offset(-px, -py)
    r.left = r.left * scaleX
    r.top = r.top * scaleY
    r.right = r.right * scaleX
    r.bottom = r.bottom * scaleY
    r.offset(px, py)
}



fun RectF.scaleCenter(scaleX: Float, scaleY: Float=scaleX) = RectExt.scaleRectFAboutCenter(this,scaleX,scaleY)

/**
 * get the relative rect of the [Rect] according to the [otherRect] ,considering the [otherRect]'s left and top is zero
 */
fun Rect.relativeTo(otherRect: Rect): Rect {
    val relativeLeft = left - otherRect.left
    val relativeTop = top - otherRect.top
    val relativeRight = relativeLeft + right - left
    val relativeBottom = relativeTop + bottom - top
    return Rect(relativeLeft, relativeTop, relativeRight, relativeBottom)
}

/**
 * Updates the coordinates of this [RectF].
 */
fun RectF.updateBounds(
    left: Float = this.left,
    top: Float = this.top,
    right: Float = this.right,
    bottom: Float = this.bottom,
) {
    set(left, top, right, bottom)
}

/**
 * Increments the coordinates of this [RectF] by the provided values.
 */
fun RectF.updateBy(
    left: Float = 0f,
    top: Float = 0f,
    right: Float = 0f,
    bottom: Float = 0f,
) {
    set(
        this.left + left,
        this.top + top,
        this.right + right,
        this.bottom + bottom,
    )
}

/**
 * Sets the coordinates of this [RectF] to the provided values converted to [Float]s.
 */
fun RectF.set(
    left: Number,
    top: Number,
    right: Number,
    bottom: Number,
) {
    set(left.toFloat(), top.toFloat(), right.toFloat(), bottom.toFloat())
}

/**
 * Sets all coordinates of this [RectF] to 0.
 */
fun RectF.clear() {
    set(0, 0, 0, 0)
}

/**
 * Applies the provided coordinates to this [RectF] and rotates it by the given number of degrees.
 */
fun RectF.setAndRotate(
    left: Number,
    top: Number,
    right: Number,
    bottom: Number,
    rotationDegrees: Float,
): RectF {
    set(left.toFloat(), top.toFloat(), right.toFloat(), bottom.toFloat())
    return rotate(rotationDegrees)
}

/**
 * Creates a new [RectF] with the same coordinates as this [RectF] without modifying this [RectF].
 */
fun RectF.copy(): RectF = RectF(this)

/**
 * Creates a [RectF] representing the bounding box of this [RectF] rotated by the provided number of degrees.
 */
fun RectF.rotate(degrees: Float): RectF {
    when {
        degrees % PI_RAD == 0f -> Unit
        degrees % 0.5f.piRad == 0f -> {
            if (width() != height()) {
                set(
                    left = centerX() - height().half,
                    top = centerY() - width().half,
                    right = centerX() + height().half,
                    bottom = centerY() + width().half,
                )
            }
        }
        else -> {
            val alpha = Math.toRadians(degrees.toDouble())
            val sinAlpha = sin(alpha)
            val cosAlpha = cos(alpha)

            val newWidth = abs(width() * cosAlpha) + abs(height() * sinAlpha)
            val newHeight = abs(width() * sinAlpha) + abs(height() * cosAlpha)

            set(
                left = centerX() - newWidth.half,
                top = centerY() - newHeight.half,
                right = centerX() + newWidth.half,
                bottom = centerY() + newHeight.half,
            )
        }
    }

    return this
}

/**
 * Moves this [RectF] horizontally and vertically by the specified distances.
 */
fun RectF.translate(x: Float, y: Float): RectF = apply {
    left += x
    top += y
    right += x
    bottom += y
}

/**
 * Returns [RectF.left] if [isLtr] is true, and [RectF.right] otherwise.
 */
fun RectF.getStart(isLtr: Boolean): Float = if (isLtr) left else right

/**
 * Returns [RectF.right] if [isLtr] is true, and [RectF.left] otherwise.
 */
fun RectF.getEnd(isLtr: Boolean): Float = if (isLtr) right else left

val RectF.leftTopPoint: PointF get() = PointF(left,top)

val RectF.leftCenterPoint:PointF get() = PointF(left,top+height().half)

val RectF.leftBottomPoint:PointF get() = PointF(left,bottom)

val RectF.centerTopPoint:PointF get() = PointF(left+width().half,top)
val RectF.centerPoint:PointF get() = PointF(left+width().half,top+height().half)
val RectF.centerBottomPoint:PointF get() = PointF(left+width().half,bottom)

val RectF.rightTopPoint:PointF get() = PointF(right,top)

val RectF.rightCenterPoint:PointF get() = PointF(right,top+height().half)

val RectF.rightBottomPoint:PointF get() = PointF(right,bottom)

val RectF.width:Float get() = abs(right-left)

val RectF.height:Float get() = abs(bottom-top)

val RectF.toSize: Size get() = Size(width ,height)
